package com.pengyeah.bigbitmapcanary

import android.app.Activity
import android.app.Application
import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.os.Bundle
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import com.pengyeah.bigbitmapcanary.constant.Constant
import com.pengyeah.bigbitmapcanary.utils.*
import com.pengyeah.bigbitmapcanary.utils.checkMainThread
import com.pengyeah.bigbitmapcanary.utils.checkNotMainThread
import com.pengyeah.bigbitmapcanary.utils.findActivityNameByView
import com.pengyeah.bigbitmapcanary.utils.findViewIDNameByView
import com.pengyeah.bigbitmapcanary.utils.showToast
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.io.File
import java.util.*
import kotlin.collections.ArrayList

object BitmapCanary {
    lateinit var context: Application

    var isOpenLog: Boolean = true
    var isWriteLog: Boolean = true

    var rate: Float = 1.2F

    fun install(ctx: Application) {
        checkMainThread()
        context = ctx
        registerActivityLifecycleCallback(context)
        LogUtils.isOpenLog = isOpenLog
    }

    private fun registerActivityLifecycleCallback(application: Application) {
        application.registerActivityLifecycleCallbacks(object :
            Application.ActivityLifecycleCallbacks {
            override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
                Log.i(Constant.TAG, "onActivityCreated:" + activity::class.java.name)
            }

            override fun onActivityStarted(activity: Activity) {
                Log.i(Constant.TAG, "onActivityStarted:" + activity::class.java.name)
            }

            override fun onActivityResumed(activity: Activity) {
                Log.i(Constant.TAG, "onActivityResumed:" + activity::class.java.name)
            }

            override fun onActivityPaused(activity: Activity) {
                Log.i(Constant.TAG, "onActivityPaused:" + activity::class.java.name)
            }

            override fun onActivityStopped(activity: Activity) {
                Log.i(Constant.TAG, "onActivityStopped:" + activity::class.java.name)
                val childViews: List<View> = getAllChildViews(activity.window.decorView)
                Log.i(Constant.TAG, "$childViews")
                checkBitmapIsTooBig(childViews)
            }

            override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
                Log.i(Constant.TAG, "onActivitySaveInstanceState:" + activity::class.java.name)
            }

            override fun onActivityDestroyed(activity: Activity) {
                Log.i(Constant.TAG, "onActivityDestroyed:" + activity::class.java.name)
            }

        })
    }

    private fun getAllChildViews(view: View): List<View> {
        val allChildren = ArrayList<View>()
        if (view is ViewGroup) {
            val vp: ViewGroup = view
            for (i in 0..vp.childCount) {
                val viewChild: View = vp.getChildAt(i) ?: return allChildren
                allChildren.add(viewChild)
                //再次 调用本身（递归）
                allChildren.addAll(getAllChildViews(viewChild))
            }
        }
        return allChildren
    }

    /**
     * 检查图片是否过大
     */
    private fun checkBitmapIsTooBig(views: List<View>) {
        for (view in views) {
            if (isTooBig(view)) {
                //show alert toast
                showAlertToast(
                    context,
                    findActivityNameByView(view),
                    view::class.java.simpleName,
                    findViewIDNameByView(context, view)
                )
            }
        }
    }

    /**
     * 判断view图片是否过大，重点检查ImageView
     */
    private fun isTooBig(view: View): Boolean {
        val viewName = findViewIDNameByView(context, view)
        val activityName = findActivityNameByView(view)
        if (view is ImageView) {
            if (view.drawable !is BitmapDrawable) {
                return false
            }
            val bmDrawable: BitmapDrawable? = view.drawable as BitmapDrawable?
            val bm: Bitmap? = bmDrawable?.bitmap
            bm?.let {
                if (it.width > rate * view.width || it.height > rate * view.height) {
                    logWarn(
                        activityName,
                        viewName,
                        view::class.java.simpleName,
                        it.width,
                        it.height,
                        view.width,
                        view.height
                    )
                    return true
                }
            }
        }
        //取背景图片
        val drawable = view.background
        if (drawable is BitmapDrawable) {
            val bm: Bitmap? = drawable.bitmap
            bm?.let {
                if (it.width > rate * view.width || it.height > rate * view.height) {
                    logWarn(
                        activityName,
                        viewName,
                        view::class.java.simpleName,
                        it.width,
                        it.height,
                        view.width,
                        view.height
                    )
                    return true
                }
            }
        }
        return false
    }

    private fun logWarn(
        activityName: String?,
        viewName: String?,
        viewClassName: String?,
        realWidth: Int,
        realHeight: Int,
        desiredWidth: Int,
        desiredHeight: Int
    ) {
        val warnInfo = StringBuilder("Bitmap size too large!!! ")
            .append("\n Activity Name:($activityName)")
            .append("\n View Id Name:($viewName)")
            .append("\n View Class Name:($viewClassName)")
            .append("\n real width: ($realWidth)")
            .append("\n real height: ($realHeight)")
            .append("\n desired width: ($desiredWidth)")
            .append("\n desired height: ($desiredHeight)")
            .toString()
        Log.i(Constant.TAG, warnInfo)


        write2Log(context, warnInfo)
    }

    /**
     * 弹出警告吐司
     */
    private fun showAlertToast(
        context: Context,
        activityName: String?,
        viewClassName: String? = "",
        viewIdName: String? = ""
    ) {
        checkMainThread()
        showToast(
            context,
            "Bitmap is too big!!!\n" +
                    "activityName:$activityName;\n" +
                    "viewClassName:$viewClassName\n" +
                    "viewIdName:$viewIdName"
        )
    }

    /**
     * 写文件记录大图片
     */
    private fun write2Log(context: Context, content: String) {
        if (!isWriteLog) {
            return
        }
        //写文件
        GlobalScope.launch(Dispatchers.IO) {
            val filePath =
                context.applicationContext.filesDir.path + File.separator + Constant.TAG + "_" + formatDate(
                    Date()
                ) + ".log"
            LogUtils.i(Constant.TAG, "filePath==>$filePath")
            writeLog(filePath, content)
        }
    }
}
